﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using System.Runtime.Remoting.Messaging;
using PlainOldVersionControl.Domain;
using System.Data.SQLite;
using System.IO;

namespace PlainOldVersionControl.Data
{
    public static class DA
    {
        #region ReadOnly Properties

        private readonly static string txlvl = "transaction_level";

        #endregion

        #region Properties

        private static string projectName = "PlainOldVersionControl";
        private static ISession Session
        {
            get
            {
                return PlainSessionFactory.Session;
            }
        }
        static int level
        {
            get
            {
                if (CallContext.GetData(txlvl) != null)
                {
                    return (int)CallContext.GetData(txlvl);
                }
                else
                {
                    CallContext.SetData(txlvl, 0);
                    return 0;
                }
            }
            set
            {
                CallContext.SetData(txlvl, value);
            }
        }

        #endregion

        #region Private Static Methods

        internal static IQuery GetHqlQuery(string hql)
        {
            if (Session.Transaction.IsActive)
            {
                return Session.CreateQuery(hql);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }
        internal static ISQLQuery GetSqlQuery(string sql)
        {
            if (Session.Transaction.IsActive)
            {
                return Session.CreateSQLQuery(sql);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }

        #endregion

        #region Public Static Methods

        internal static void Begin()
        {
            PlainSessionFactory.BeginTransaction();
            level++;
        }
        internal static void Commit()
        {
            if (Session.Transaction.IsActive && level == 1)
            {
                Session.Flush();
                Session.Transaction.Commit();
                Session.Close();
            }
            level--;
        }
        internal static void RollBack()
        {
            try
            {
                if (Session.Transaction.IsActive && !Session.Transaction.WasRolledBack)
                {
                    Session.Transaction.Rollback();
                    Session.Close();
                }
                level--;
            }
            catch (TransactionException ex)
            {
                if (ex.Message.Equals("Transaction not connected, or was disconnected"))
                {
                    level = 0;
                    Session.Close();
                    throw new ValidationException("You lost your connection to the database during your last task.  You can retry or restart the program.");
                }
                throw;
            }
        }
        public static void Save(object obj)
        {
            if (Session.Transaction.IsActive)
            {
                Session.SaveOrUpdate(obj);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }
        public static void Delete(object obj)
        {
            if (Session.Transaction.IsActive)
            {
                Session.Delete(obj);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }

        public static T LoadByID<T>(int id)
        {
            if (Session.Transaction.IsActive)
            {
                return Session.Load<T>(id, LockMode.Read);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }

        public static T GetById<T>(string hash)
        {
            if (Session.Transaction.IsActive)
            {
                return Session.Get<T>(hash);
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }
        public static IList<T> GetAll<T>()
        {
            if (Session.Transaction.IsActive)
            {
                return Session.CreateCriteria(typeof(T)).List<T>();
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }
        public static Type GetType(object DomainObject)
        {
            return NHibernateUtil.GetClass(DomainObject);
        }

        public static void Initialize(object obj)
        {
            if (!NHibernateUtil.IsInitialized(obj))
                NHibernateUtil.Initialize(obj);
        }

        public static void BuildFactory()
        {
            PlainSessionFactory.BuildFactory();
        }
        
        public static string GetCurrentDB()
        {
            return PlainSessionFactory.DatabaseServer;
        }

        public static void Evict(object @object)
        {
            Session.Evict(@object);
        }
        #endregion

        public static SQLiteCommand GetCommand()
        {
            return Session.Connection.CreateCommand() as SQLiteCommand;
        }

        public static bool DBExists()
        {
            return File.Exists(Settings.CurrentDatabase);
        }

        internal static ICriteria CreateCriteria<T>()
        {
            if (Session.Transaction.IsActive)
            {
                return Session.CreateCriteria(typeof(T));
            }
            else
            {
                throw new InvalidOperationException(string.Format("All data access in {0} must be in a transaction", projectName));
            }
        }
    }
}
